29 марта 2024

Блог

Разработка

Брокер сообщений Rabbit MQ: как определиться с типом очередей

Если вы выбрали RabbitMQ в качестве брокера сообщений, то не пренебрегайте и выбором подходящего типа очереди. Тип, выбранный по умолчанию, может привести к непредсказуемым последствиям.

Начнем с базы

RabbitMQ – это брокер сообщений, который используется для выполнения отложенных или асинхронных задач. Действует это примерно так: есть приложение, которое кладет полученное сообщение в «очередь» - publisher и есть приложение, которое собирает это сообщение – consumer, и, в соответствии с ним, выполняет какое-то действие. 

Самый простой пример использования: на сайте пользователь набирает сообщение и нажимает копку «Отправить», которая должна переадресовать текст сообщения на электронную почту компании. Сайт положит это сообщение в брокер, например, в RabbitMQ, а какой-нибудь специальный микросервис заберет его из очереди и отправит на электронную почту в удобное время (асинхронно).

Базовые термины:

  • Очередь (Queue) - базовая сущность, очередь необработанных сообщений
  • Exchange -точка маршрутизации сообщений, входящие сообщения попадают сначала в exchange.
  • Binding - связь между exchange и queue.
  • Publisher - внешнее приложение, генерирующее сообщения в очередь
  • Consumer - приложение-обработчик, получающий сообщения из очереди
  • Message - сообщение, несущее полезную нагрузку. Проходит весь путь от Publisher до Consumer.

Рэббит

 

Дилемма

Разработчики в своей работе сталкиваются с дилеммой – какой брокер сообщений выбрать. Эта дилемма не нова. Существуют два самых популярных брокера – Kafka и RabbitMQ. О том, что из этого лучше, пишут много и давно. У каждого из них свои особенности и свои кейсы применения, и выбор того или иного варианта зависит от разных факторов. Например, Kafka чаще используют для обмена сообщениями между несколькими проектами, а для обмена внутри одного проекта предпочитают использовать RabbitMQ.

У нас есть опыт работы и с тем, и с другим брокером сообщений. Но больше, чем на половине наших продуктов используется RabbitMQ. И в данной статье мы хотим затронуть дилемму не менее важную.

Часто разработчики, выбирая RabbitMQ, не задумываются о типе очередей, с которым будут работать, выбирая его по умолчанию. После этого есть риск столкнуться с проблемой – обмен сообщениями происходит непредсказуемо, не приводит к ожидаемым результатам, а иногда данные попросту теряются.

Наши специалисты DevOps провели исследование и сделали подробный разбор каждого типа, чем мы и хотим поделиться.

Типы очередей:

  1. Classic. Классические очереди FIFO. Они быстрые и не требовательны к ресурсам. Их используют в топологиях, где важна высокая доступность сервиса. Из минусов - непродуманный механизм репликации (mirroring), что может приводить к потере данных.
  2. Quorum. Реплицируемые FIFO-очереди, основанные на RAFT-алгоритме. Их используют для топологий, где очереди существуют продолжительное время и сохранность данных важнее, чем доступность сервиса.
  3. Stream. Персистентная и реплицируемая структура данных (не совсем очередь 😊), представляющая собой append-only журнал. Могут использовать для доставки сообщения нескольким подписчикам (без необходимости создания отдельной очереди для каждого). После прочтения консьюмером сообщения не удаляются сразу и могут быть перепрочитаны. 

CAP-теорема

CAP – это акроним из трех понятий: Consistency (Согласованность), Availability (Доступность) и Partition tolerance (Устойчивость к разделению). Согласно этой теореме, в распределенной системе нельзя одновременно добиться всех этих свойств. В реализации распределенных вычислений возможно обеспечить не более двух.

Рассмотрим каждый из типов очередей в разрезе каждого из этих свойств.

Согласованность

  • Classic. В случае данного типа ее нет. По умолчанию классические очереди не реплицируются и сохраняются только на одной ноде кластера. Есть специальный механизм зеркалирования (репликации), НО он объявлен как deprecated, то есть устарел и в новой версии будет удален. Кроме того, зеркалирование нужно настраивать отдельно и работает оно не самым прозрачным образом, что часто приводит к потере данных.
  • Quorum. Согласованность достигается за счет RAFT-алгоритма на основе кворума. Если количество «живых» инстансов в кластере недостаточно для уверенности в сохранности данных, то RabbitMQ приостановит обработку сообщений.
  • Stream. В stream-очередях также дейстует RAFT-алгоритм. В этом они похожи на кворумные очереди.

Доступность

  • Classic. Умеют обрабатывать падения узлов кластера только при настройке механзима зеркалирования. В случае падения лидера очереди новым лидером становится самое старое «зеркало». Есть много особенностей, и восстановление после сбоя требует полной ресинхронизации, что может привести к долгому периоду недоступности очереди.
  • Quorum. Спокойно обрабатывают падение узлов кластера, пока сохраняется quorum (большинство). В случае выхода из строя большинства узлов - приостанавливают обработку сообщений. После восстановления узла происходит частичная ресинхронизация (гораздо быстрее).
  • Stream. Поведение похоже на quorum-очереди

Устойчивость к разделению

  • Classic. Механизм реакции на сетевое разделение настраивается отдельно через параметр cluster_partition_handling и по умолчанию требует вмешательства человека для обработки сбоя. Очень часто приводит к ситуации «Split-Brain» - когда активный и пассивный серверы попытаются взять на себя роль активного сервера, что что может привести к несогласованности данных.
  • Quorum. В случае сетевого разделения в рабочем состоянии остается только та часть, в которой сохранилось большинство. Другие части кластера приостанавливают свою работу до восстановления связи.
  • Stream. Поведение также похоже на quorum-очереди.

Выводы

Нельзя сказать, что есть вариант лучше или хуже. Все зависит исключительно от ваших потребностей:

  • Если вам важна доступность брокера и при этом не так важна сохранность сообщений - используйте классические очереди. Но помните, что механизм зеркалирования объявлен как «deprecated» и будет удален в 4-ой версии RabbitMQ.
  • Если сохранность данных важнее доступности брокера - используйте кворумные очереди.
  • А если вам нужен функционал подобный функционалу Kafka - присмотритесь к stream.